package com.maben.security.filter;

import com.maben.security.util.JwtUtils;
import io.jsonwebtoken.JwtException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;

/**
 * token校验过滤器
 *   过滤器只负责 “验证并设置认证信息”，不负责 “阻止访问”；
 *   “是否允许访问” 完全由后续的授权规则（permitAll()、hasRole() 等）决定
 */
@Slf4j
public class JwtAuthenticationFilter extends OncePerRequestFilter {
    private final JwtUtils jwtUtils;
    private final UserDetailsService userServiceImpl;

    // 构造器注入（必须，因为不再有 @Component 自动注入，如果标注了，会自动加入spring过滤器，导致security的规则失败）
    public JwtAuthenticationFilter(JwtUtils jwtUtils, UserDetailsService userServiceImpl) {
        this.jwtUtils = jwtUtils;
        this.userServiceImpl = userServiceImpl;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        // 1. 从请求头获取 Token（格式：Bearer <token>）
        String authHeader = request.getHeader("Authorization");
        String token = null;
        String username = null;
        log.info("请求路径：{}", request.getRequestURI());
        if (authHeader != null && authHeader.startsWith("Bearer ")) {
            token = authHeader.substring(7); // 截取 "Bearer " 后的 token
        }
        // 2. 若 Token 存在，先验证有效性，再解析用户名
        if (StringUtils.isNotBlank(token)) {
            try {
                // 先验证 Token 有效性（包含过期、签名等校验）
                if (jwtUtils.validateToken(token)) {
                    // 验证通过后，再解析用户名
                    username = jwtUtils.getUsernameFromToken(token);
                    // 若用户名存在且未认证，则设置认证信息
                    if (StringUtils.isNotBlank(username) && SecurityContextHolder.getContext().getAuthentication() == null) {
                        UserDetails userDetails = userServiceImpl.loadUserByUsername(username);
                        UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
                                userDetails, null, userDetails.getAuthorities()
                        );
                        authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                        SecurityContextHolder.getContext().setAuthentication(authToken);

                        // 打印认证信息日志
                        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
                        List<String> authRoles = auth.getAuthorities().stream()
                                .map(GrantedAuthority::getAuthority)
                                .collect(Collectors.toList());
                        log.info("设置认证信息成功：用户名={}，权限={}", auth.getName(), authRoles);
                    }
                } else {
                    log.warn("Token 无效（未通过验证）");
                }
            } catch (JwtException e) {
                // 捕获所有 JWT 相关异常（过期、签名错误、格式错误等）
                log.warn("Token 处理失败，原因：{}", ExceptionUtils.getStackTrace(e));
                // 无需设置认证信息，让后续流程处理未认证状态
            } catch (Exception e) {
                // 捕获其他意外异常
                log.error("Token 处理发生未知错误：", ExceptionUtils.getStackTrace(e));
            }
        }
        // 继续执行后续过滤器
        filterChain.doFilter(request, response);
    }
}